package org.pt.utils;

import org.pt.dto.InvitationDto;
import org.pt.exception.InvitationException;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static org.pt.config.Constants.IVCODE_EXPIRE_DAY;

@Component
public class InvitationCodeUtil {

    private static final int CODE_LENGTH = 6; // 邀请码长度
    private static final int CODE_MIN_VALUE = (int) Math.pow(10, CODE_LENGTH - 1);
    private static final int CODE_MAX_VALUE = (int) (Math.pow(10, CODE_LENGTH) - 1);
    private static final String INVITER_PREFIX = "invitation:inviter:";
    private static final String CODE_PREFIX = "invitationCode:";

    private final RedisTemplate<String, Object> redisTemplate;

    public InvitationCodeUtil(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 生成邀请码
     *
     * @param inviterId 邀请人ID
     * @param isAdmin   是否为管理员
     * @return 生成的邀请码信息
     */
    public InvitationDto generateInvitationCode(int inviterId, boolean isAdmin,int remaining) throws InvitationException {
        String inviterIdStr = String.valueOf(inviterId);
        String inviterKey = INVITER_PREFIX + inviterIdStr;

        if (redisTemplate.hasKey(inviterKey)) {
            String existingCode = (String) redisTemplate.opsForValue().get(inviterKey);
            return new InvitationDto(inviterId,remaining,existingCode);
        }

        String invitationCode = generateUniqueCode();
        String invitationCodeKey = CODE_PREFIX + invitationCode;

        remaining = isAdmin ? remaining : 1; // 管理员无限次，普通用户1次

        // 存储邀请人与邀请码的映射关系
        redisTemplate.opsForValue().set(inviterKey, invitationCode);

        // 存储邀请码的详细信息
        HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash();
        hashOps.put(invitationCodeKey, "inviter", inviterIdStr);
        hashOps.put(invitationCodeKey, "invitationCode", invitationCode);
        hashOps.put(invitationCodeKey, "remaining", remaining);

        // 设置过期时间（假设有效期为30天）
        redisTemplate.expire(inviterKey,IVCODE_EXPIRE_DAY , TimeUnit.DAYS);
        redisTemplate.expire(invitationCodeKey, IVCODE_EXPIRE_DAY, TimeUnit.DAYS);

        return new InvitationDto(inviterId, remaining, invitationCode);
    }

    /**
     * 验证邀请码并减少使用次数
     *
     * @param invitationCode 邀请码
     * @return 验证结果
     */
    public InvitationDto validateInvitationCode(String invitationCode) throws InvitationException {
        String invitationCodeKey = CODE_PREFIX + invitationCode;

        // 检查邀请码是否存在
        if (!redisTemplate.hasKey(invitationCodeKey)) {
            throw new InvitationException("邀请码不存在或已过期");
        }

        HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash();

        // 使用 Lua 脚本执行原子操作
        String luaScript = """
    local remaining = tonumber(redis.call('hget', KEYS[1], 'remaining'))
    local inviter = redis.call('hget', KEYS[1], 'inviter')
    redis.call('hincrby', KEYS[1], 'remaining', -1)
    
    if remaining-1 == 0 then
        redis.call('del', 'invitation:inviter:' .. inviter)
        redis.call('del', KEYS[1])
    end
    return {remaining-1, inviter};
    
    """;

        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>(luaScript, List.class);
        List<Object> result = redisTemplate.execute(redisScript, Collections.singletonList(invitationCodeKey));

        // 安全解析结果
        if (result == null || result.size() < 2) {
            throw new InvitationException("邀请码解析失败");
        }

        int updatedRemaining = ((Number) result.get(0)).intValue();
        String inviterIdStr = result.get(1) != null ? result.get(1).toString() : null;

        if (updatedRemaining == 0 || inviterIdStr == null) {
            redisTemplate.delete(INVITER_PREFIX+inviterIdStr);
        }

        return new InvitationDto(
                Integer.parseInt(inviterIdStr),
                updatedRemaining,
                invitationCode
        );
    }


    /**
     * 生成唯一的邀请码
     *
     * @return 唯一的邀请码
     */
    private String generateUniqueCode() {
        Random random = new Random();
        String invitationCode;

        do {
            invitationCode = String.valueOf(random.nextInt(CODE_MAX_VALUE - CODE_MIN_VALUE + 1) + CODE_MIN_VALUE);
        } while (redisTemplate.hasKey(CODE_PREFIX + invitationCode));

        return invitationCode;
    }
}
